package cuatroenraya.jugadores;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.util.ArrayList;
import java.util.Random;
import cuatroenraya.Jugador;

/**
 *
 * @author juan
 */
public final class JugadorAlumnoG11 extends Jugador {

    private static Random random;

    private static synchronized double getRandom() {
        if (random == null) {
            random = new Random();
        }
        return random.nextDouble();
    }

    /**
     *
     * @param El tablero
     * @return
     */
    public int[] jugar(int[][] tablero) {
        int[] mov = this.bloquear(tablero);
        if (mov == null) {
            mov = primeroElMejor(tablero);
        }
        return mov;
    }

    /**
     * devuelve donde hay que bloquar a las tres fichas alineadas del adversario
     */
    public int[] bloquear(int[][] tablero) {

        int PosicionInsertada = 0;

        for (int i = 0; i < tablero.length; i++) {
            for (int j = 0; j < tablero[i].length; j++) {
                if (tablero[i][j] == 1) {

                    //Chequeo Linea Horizontal
                    PosicionInsertada = this.chequearLineaHorizontal(tablero, i, j);

                    if (PosicionInsertada == 0) {

                        //Chequeo Linea Vertical
                        PosicionInsertada = this.chequearLineaVertical(tablero, i, j);

                        if (PosicionInsertada == 0) {

                            //Chequeo Diagonal
                            PosicionInsertada = this.chequearLineaDiagonal(tablero, i);

                            if (PosicionInsertada == 0) {
                                ///this.coordinates = getBestPosition();
                                return null;
                            }

                        } else {

                            int[] v = new int[2];
                            v[0] = i;
                            v[1] = PosicionInsertada;
                            //this.coordinates = v;
                            return v;
                        }
                    } else {
                        if (PosicionInsertada == 10) {
                            //this.coordinates = getBestPosition();
                            return null;
                        } else {
                            int[] v = new int[2];
                            v[0] = PosicionInsertada;
                            v[1] = j;
                            //this.coordinates = v;
                            return v;
                        }
                    }
                }
            }
        }
        return null;
    }

    public int chequearLineaHorizontal(int[][] tablero, int x, int y) {

        int PosicionInsertar = 0;
        int cuentaPosicionInsertar = 0;
        int cuentaFichaContraria = 0;
        int cuentaFichaNuestra = 0;

        for (int i = x; i < 4; i++) {

            //printTablero();
            switch (tablero[i][y]) {

                // Libre
                case 0:
                    cuentaPosicionInsertar++;
                    PosicionInsertar = i;
                    break;

                //Ficha Contraria
                case 1:
                    cuentaFichaContraria++;
                    break;

                //Ficha Nuestra
                case 2:
                    cuentaFichaNuestra++;
                    break;
            }

        }

        if (cuentaFichaContraria == 3) {
            return PosicionInsertar;
        } else {
            return 0;
        }
    }

    /**
     * Selecciona la Posici�n para evitar un 4 en L�nea Vertical
     * @param x:
     * @param y:
     * @return
     */
    private int chequearLineaVertical(int tablero[][], int x, int y) {

        int PosicionInsertar = 0;
        int cuentaPosicionInsertar = 0;
        int cuentaFichaContraria = 0;
        int cuentaFichaNuestra = 0;

        for (int j = y; j < (y + 4); j++) {
            switch (tablero[x][j]) {

                // Libre
                case 0:
                    cuentaPosicionInsertar++;
                    PosicionInsertar = j;
                    break;

                //Ficha Contraria
                case 1:
                    cuentaFichaContraria++;
                    break;

                //Ficha Nuestra
                case 2:
                    cuentaFichaNuestra++;
                    break;


            }
        }

        if (cuentaFichaContraria == 3) {
            return PosicionInsertar;
        } else {
            return 0;
        }
    }

    /**
     * Selecciona la Posici�n para evitar un 4 en L�nea Diagonal
     * @param x:
     * @param y:
     * @return
     */
    private int chequearLineaDiagonal(int[][] tablero, int x) {

        int PosicionInsertar = 0;
        int cuentaPosicionInsertar = 0;
        int cuentaFichaContraria = 0;
        int cuentaFichaNuestra = 0;

        for (int i = x; i < (x + 4); i++) {
            switch (tablero[i][i]) {

                // Libre
                case 0:
                    cuentaPosicionInsertar++;
                    PosicionInsertar = i;
                    break;

                //Ficha Contraria
                case 1:
                    cuentaFichaContraria++;
                    break;

                //Ficha Nuestra
                case 2:
                    cuentaFichaNuestra++;
                    break;

            }
        }

        if (cuentaFichaContraria == 3) {
            return PosicionInsertar;
        } else {
            return 0;
        }
    }

    private int[] primeroElMejor(int[][] tablero) {
        ArrayList<InternalTablero> movimientosPosible = new ArrayList<InternalTablero>();
        for (int i = 0; i < tablero.length; i++) {
            for (int j = 0; j < tablero[i].length; j++) {
                if (tablero[i][j] == 0) {
                    int[] coordinates = new int[2];
                    coordinates[0] = i;
                    coordinates[1] = j;
                    InternalTablero posible = new InternalTablero(tablero, coordinates);
                    movimientosPosible.add(posible);
                }
            }
        }
        InternalTablero elegido = null;

        for (InternalTablero posible : movimientosPosible) {
            if (elegido == null || elegido.getValue() > posible.getValue()) {
                elegido = posible;
            }
        }
        return elegido.getCoordinates();
    }

    private class InternalTablero {

        private int[][] tablero;
        private int[] coordinates;

        /**
         * Constructor de la Clase, que inicializa el tablero local
         * @param t: Tablero actual
         * @param coordinates: Coordenadas donde pondremos nuestra ficha
         */
        public InternalTablero(int[][] t, int[] coordinates) {
            this.tablero = new int[t.length][];
            for (int i = 0; i < t.length; i++) {
                this.tablero[i] = new int[t[i].length];
                for (int j = 0; j < t[i].length; j++) {
                    if (coordinates[0] == i && coordinates[1] == j) {
                        this.tablero[i][j] = 3;
                    } else {
                        this.tablero[i][j] = t[i][j];
                    }
                }
                this.coordinates = coordinates;
            }
        }

        /**
         * Coordenadas de la jugada actual
         * @return: Devuelve Coordenadas de la jugada actual
         */
        public int[] getCoordinates() {
            return this.coordinates;
        }

        /**
         * M�trica para Comparar entre las diferentes opciones de jugada a realizar
         * @return: Devuelve M�trica para Comparar entre las diferentes opciones de jugada a realizar
         * teniendo en cuenta posiciones centrales (donde dan mas jugadas futuras de movimiento)
         * y distancias hasta fichas, propias, ajenas y celdas vacias.
         * Ademas de esto agrega un peque�o random que le quita un porcentaje para
         * que la suerte tambien este presente
         */
        public double getValue() {
            double value = this.getCenterValue();
            value -= this.myTokenDistanceValue() * 10;
            value -= this.myTokenZerosDistanceValue() * 40;
            value += this.myTokenOthersDistanceValue() * 60;
            value = value * getRandom();
            return Math.abs(value);
        }

        /**
         * Valor Central Aplicando la Transformada de Hough
         * @return: Devuelve el Valor Central Aplicando la Transformada de Hough
         */
        private double getCenterValue() {
            double value = 0;
            int rows = tablero.length;
            int columns = tablero[1].length;
            int[][] mask = Hough.getCenterWMask(rows, columns);
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < columns; j++) {
                    switch (tablero[i][j]) {
                        case 3:
                            value += mask[i][j];
                            break;
                        default:
                            break;
                    }//siwtch
                }//for j
            }//for i
            return value;
        }

        /**
         * Promedio de las distancias entre las fichas de nuestro jugador
         * @return
         */
        private double myTokenDistanceValue() {
            double count = 0;
            double value = 0;
            for (int i = 0; i < this.tablero.length; i++) {
                for (int j = 0; j < this.tablero[i].length; j++) {
                    if (tablero[i][j] == 1) {
                        int[] t = new int[2];
                        t[0] = i;
                        t[1] = j;
                        value += eDistance(coordinates, t);
                        count++;
                    }
                }
            }

            return value / count;
        }

        /**
         * Promedio de las Distancias a los Valores Vac�os
         * @return: Devuelve Promedio de las Distancias a los Valores Vac�os
         */
        private double myTokenZerosDistanceValue() {
            double count = 0;
            double value = 0;
            for (int i = 0; i < this.tablero.length; i++) {
                for (int j = 0; j < this.tablero[i].length; j++) {
                    if (tablero[i][j] == 0) {
                        int[] t = new int[2];
                        t[0] = i;
                        t[1] = j;
                        value += eDistance(coordinates, t);
                        count++;
                    }
                }
            }
            return value / count;
        }

        /**
         * Promedio de Distancias a las fichas del Otro Jugador
         * @return: Devuelve Promedio de Distancias a las fichas del Otro Jugador
         */
        private double myTokenOthersDistanceValue() {
            double count = 0;
            double value = 0;
            for (int i = 0; i < this.tablero.length; i++) {
                for (int j = 0; j < this.tablero[i].length; j++) {
                    if (tablero[i][j] == 2) {
                        int[] t = new int[2];
                        t[0] = i;
                        t[1] = j;
                        value += eDistance(coordinates, t);
                        count++;
                    }
                }
            }
            return value / count;
        }

        /**
         * C�lculo de Distancia Eucl�dea
         * @param f: vector destino
         * @param t: vector origen
         * @return: Devuelve la distancia eucl�dea entre dos puntos
         */
        private double eDistance(int[] f, int[] t) {
            double xcuad = Math.pow(f[0] - t[0], 2);
            double ycuad = Math.pow(f[1] - t[1], 2);
            return Math.sqrt(xcuad + ycuad);
        }
    }

    /**
     * Clase que genera una mascara dandole mayores valores a los centrales
     */
    private static class Hough {

        private static int[][] getCenterWMask(int r, int c) {
            int[][] mask = new int[r][c];
            int[] row_incs = increment(r);
            int[] col_incs = increment(c);
            for (int i = 0; i < r; i++) {
                for (int j = 0; j < c; j++) {
                    mask[i][j] = row_incs[i] + col_incs[j];

                }
            }
            return mask;
        }

        private static int[] increment(int n) {
            int[] incs = new int[n];
            int inc = 0;
            for (int i = 0; i < n; i++) {
                if (i < n / 2 || (n % 2 != 0 && i == n / 2)) {
                    inc++;
                } else if (i > n / 2) {
                    inc--;
                }
                incs[i] = inc;
            }
            return incs;
        }

        public static void main(String[] args) {
            for (int[] row : getCenterWMask(8, 8)) {
                for (int col : row) {
                    System.out.print(col);
                }
                System.out.print("\n");
            }
        }
    }
}

